package com.xzj.common.core.utils;

import com.xzj.common.core.domain.entity.BaseTreeEntity;
import com.xzj.common.core.domain.entity.TreeEntity;
import com.xzj.common.core.function.ValueFromTFun;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @program: xzj-admin
 * @ClassName: TreeUtil
 * @description: 树形结构处理方法
 * @author: XiaZunJun
 * @create: 2024-06-26 08:57
 */
public class TreeUtil {

    /**
     * 将继承自 BaseTreeEntity 的泛型类型列表转换为树状结构的列表
     * 本方法假设列表中的每个元素都有一个 getId() 和 getParentId() 方法，用于获取其唯一标识符和父标识符
     * 如果元素的 parentId 为空或者等于 "0"，则将其视为根节点添加到结果列表中
     * 否则，尝试在已构建的树状结构中找到其父节点，并将当前元素添加到其父节点的子节点列表中
     * 如果父节点不存在于树状结构中（即父 ID 标识符 在 treeMap 中不存在），则将当前元素添加到结果列表中，作为一个孤立节点
     *
     * @param sourceList 要转换的列表
     * @return 转换后的树状结构列表
     */
    public static <T extends BaseTreeEntity<T>> List<T> listToTree(List<T> sourceList){
        List<T> resultList = new ArrayList<T>();
        Map<String,T> treeMap = new HashMap<>();
        for(T t : sourceList){
            treeMap.put(t.getId(),t);
        }
        for(T t : sourceList){
            String pid = t.getParentId();
            if(null == pid || pid.equals("0")){
                resultList.add(t);
            }else{
                if(treeMap.containsKey(pid)){
                    treeMap.get(pid).getChildren().add(t);
                }else{
                    resultList.add(t);
                }
            }
        }
        return resultList;
    }

    /**
     * 将列表转换为树形结构
     *
     * @param sourceList 原始的实体列表
     * @param idFun       获取主键的方法（用于唯一标识每个实体）
     * @param parentIdFun 获取父键的方法（用于标识每个实体的父级）
     * @param <T>         泛型类型，继承自TreeEntity，代表要转换的实体类型
     * @return 转换后的树形结构列表
     */
    public static <T extends TreeEntity<T>> List<T> listToTree(List<T> sourceList, ValueFromTFun<T,String> idFun, ValueFromTFun<T,String> parentIdFun){
        List<T> resultList = new ArrayList<T>();
        Map<String,T> treeMap = new HashMap<>();
        for(T t : sourceList){
            String id = idFun.apply(t);
            treeMap.put(id,t);
        }
        for(T t : sourceList){
            String pid = parentIdFun.apply(t);
            if(null == pid || pid.equals("0")){
                resultList.add(t);
            }else{
                if(treeMap.containsKey(pid)){
                    treeMap.get(pid).getChildren().add(t);
                }else{
                    resultList.add(t);
                }
            }
        }
        return resultList;
    }

    public static <T> Map<String,String> treeToMap(List<T> sourceList, ValueFromTFun<T,String> keyFun,ValueFromTFun<T,String> pidFun, ValueFromTFun<T,String> valueFun){
        Map<String,String> resultMap = new HashMap<>();
        Map<String,T> treeMap = new HashMap<>();
        for(T t : sourceList){
            String id = keyFun.apply(t);
            treeMap.put(id,t);
        }
        for(T t : sourceList){
            String pid = pidFun.apply(t);
            String key = keyFun.apply(t);
            String value = valueFun.apply(t);
            if(null == pid || pid.equals("0")){
                resultMap.put(key,value);
            }else{
                String namePath = getParentId(pid, treeMap, pidFun, valueFun);
                if(null != namePath){
                    value = namePath + "-" + value;
                }
                resultMap.put(key,value);
            }
        }
        treeMap.clear();
        return resultMap;
    }

    private static <T> String getParentId(String pid, Map<String,T> treeMap, ValueFromTFun<T,String> pidFun,ValueFromTFun<T,String> valueFun){
        String result = null;
        if(treeMap.containsKey(pid)){
            T t = treeMap.get(pid);
            String parentId = pidFun.apply(t);
            String value = valueFun.apply(t);
            result = getParentId(parentId,treeMap,pidFun,valueFun);
            if(null != result){
                result = result + "-" + value;
            }else{
                result = value;
            }
        }
        return result;
    }

}
